package com.joizhang.seckill.redis;

import com.alibaba.fastjson.JSONObject;
import com.joizhang.seckill.api.redis.KeyPrefix;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * redis服务
 */
@Service
public class RedisService {

    private final RedisTemplate<String, Object> redisTemplate;

    @Autowired
    public RedisService(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public Object get(KeyPrefix prefix, String key) {
        ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
        //对key增加前缀，即可用于分类，也避免key重复
        String realKey = prefix.getPrefix() + ":" + key;
        return valueOperations.get(realKey);
    }

    public Boolean set(KeyPrefix prefix, String key, Object value) {
        ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
        String realKey = prefix.getPrefix() + ":" + key;
        //获取过期时间
        int seconds = prefix.expireSeconds();
        if (seconds <= 0) {
            valueOperations.set(realKey, value);
        } else {
            valueOperations.set(realKey, value, seconds, TimeUnit.SECONDS);
        }
        return true;
    }

    /**
     * 删除 key
     */
    public Boolean delete(KeyPrefix prefix, String key) {
        String realKey = prefix.getPrefix() + ":" + key;
        return redisTemplate.delete(realKey);
    }

    /**
     * 删除 keys
     */
    public void deleteKeys(Set<String> keys) {
        redisTemplate.delete(keys);
    }

    /**
     * 判断 key 是否存在
     */
    public Boolean exists(KeyPrefix prefix, String key) {
        String realKey = prefix.getPrefix() + ":" + key;
        return redisTemplate.hasKey(realKey);
    }

    /**
     * 查询符合 pattern 的 key
     */
    public Set<String> keys(KeyPrefix prefix, String pattern) {
        String realKey = prefix.getPrefix() + ":" + pattern;
        return redisTemplate.keys(realKey);
    }

    /**
     * 增加值
     * Redis Incr 命令将 key 中储存的数字值增一。
     * 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 INCR 操作
     */
    public <T> Long increment(KeyPrefix prefix, String key) {
        ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
        //生成真正的key
        String realKey = prefix.getPrefix() + ":" + key;
        return valueOperations.increment(realKey);
    }

    /**
     * 减少值
     */
    public <T> Long decrement(KeyPrefix prefix, String key) {
        ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
        //生成真正的key
        String realKey = prefix.getPrefix() + ":" + key;
        return valueOperations.decrement(realKey);
    }

    public void setZSet(KeyPrefix prefix, String key, Object value, Double score) {
        ZSetOperations<String, Object> zSetOperations = redisTemplate.opsForZSet();
        String realKey = prefix.getPrefix() + key;
        zSetOperations.add(realKey, value, score);
        int seconds = prefix.expireSeconds();
        if (seconds > 0) {
            redisTemplate.expire(realKey, seconds, TimeUnit.SECONDS);
        }
    }

    public List<Object> getZSet(KeyPrefix prefix, String key) {
        ZSetOperations<String, Object> zSetOperations = redisTemplate.opsForZSet();
        String realKey = prefix.getPrefix() + key;
        Long size = zSetOperations.size(realKey);
        if (Objects.isNull(size)) {
            return null;
        }
        Set<Object> range = zSetOperations.range(realKey, 0, size);
        if (CollectionUtils.isEmpty(range)) {
            return null;
        }
        return new ArrayList<>(range);
    }

    public void setHash(KeyPrefix prefix, String key, String hashKey, Object hashValue) {
        HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();
        String realKey = prefix.getPrefix() + ":" + key;
        hashOperations.put(realKey, hashKey, hashValue);
    }

    public Object getHash(KeyPrefix prefix, String key, String hashKey) {
        HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();
        String realKey = prefix.getPrefix() + ":" + key;
        return hashOperations.get(realKey, hashKey);
    }

    public void setHashAll(KeyPrefix prefix, String key, JSONObject jsonObject) {
        HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();
        String realKey = prefix.getPrefix() + ":" + key;
        hashOperations.putAll(realKey, jsonObject);
        int seconds = prefix.expireSeconds();
        if (seconds > 0) {
            redisTemplate.expire(realKey, seconds, TimeUnit.SECONDS);
        }
    }

    public JSONObject getHashAll(KeyPrefix prefix, String key) {
        HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();
        String realKey = prefix.getPrefix() + ":" + key;
        Map<String, Object> entries = hashOperations.entries(realKey);
        return new JSONObject(entries);
    }

    public void execute(DefaultRedisScript<Long> script, KeyPrefix prefix, String key, Object... args) {
        String realKey = prefix.getPrefix() + ":" + key;
        List<String> keys = Collections.singletonList(realKey);
        redisTemplate.execute(script, keys, args);
    }

}
